#include <iostream>
#include <array>
#include <map>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>

namespace part2
{

    enum class HandType
    {
        FIVE_OF_A_KIND = 0,
        FOUR_OF_A_KIND,
        FULL_HOUSE,
        THREE_OF_A_KIND,
        TWO_PAIR,
        ONE_PAIR,
        HIGH_CARD,
    };

    class Hand
    {
        static std::map<char, int> strengthsMap;

    public:
        Hand(const std::string& line);

    public:
        HandType type() const noexcept
        { return this->_type; }

        const std::array<int, 5>& strengths() const noexcept
        { return this->_strengths; }

        int bidAmount() const noexcept
        { return this->_bidAmount; }

    private:
        HandType _type;
        std::array<int, 5> _strengths;
        int _bidAmount;
    };

    std::map<char, int> Hand::strengthsMap {
        {'A', 0}, {'K', 2}, {'Q', 3}, {'T', 5},
        {'9', 6}, {'8', 7}, {'7', 8}, {'6', 9}, {'5', 10},
        {'4', 11}, {'3', 12}, {'2', 13}, {'1', 14}, {'0', 15}, {'J', 16},
    };

    Hand::Hand(const std::string& line) :
        _type{HandType::HIGH_CARD},
        _strengths{},
        _bidAmount{0}
    {
        std::string labels {line.substr(0, 5)};
        std::map<char, int> labelSize {};
        for (std::size_t i = 0; i < 5; ++i)
        {
            char label {labels[i]};

            auto iter {labelSize.find(label)};
            if (iter == labelSize.end())
                labelSize.emplace(label, 1);
            else
                iter->second += 1;

            this->_strengths[i] = Hand::strengthsMap[label];
        }

        char minCard {labelSize.begin()->first};
        int minSize {labelSize.begin()->second};
        char maxCard {minCard};
        int maxSize {minSize};

        for (const auto& kv : labelSize)
        {
            if (kv.second < minSize)
                minSize = kv.second;
            if (kv.second > maxSize)
                maxSize = kv.second;
        }

        auto labelJ {labelSize.find('J')};

        switch (labelSize.size())
        {
            case 1:
            {
                // AAAAA
                this->_type = HandType::FIVE_OF_A_KIND;
                break;
            }
            case 2:
            {
                if (minSize == 1)
                {
                    // AAAAB
                    if (labelJ != labelSize.end())
                        this->_type = HandType::FIVE_OF_A_KIND;
                    else
                        this->_type = HandType::FOUR_OF_A_KIND;
                }
                else
                {
                    // AAABB
                    if (labelJ != labelSize.end())
                        this->_type = HandType::FIVE_OF_A_KIND;
                    else
                        this->_type = HandType::FULL_HOUSE;
                }
                break;
            }
            case 3:
            {
                if (maxSize == 3)
                {
                    // AAABC
                    if (labelJ == labelSize.end())
                        this->_type = HandType::THREE_OF_A_KIND;
                    else
                        this->_type = HandType::FOUR_OF_A_KIND;
                }
                else
                {
                    // AABBC
                    if (labelJ == labelSize.end())
                        this->_type = HandType::TWO_PAIR;
                    else if (labelJ->second == 2)
                        this->_type = HandType::FOUR_OF_A_KIND;
                    else
                        this->_type = HandType::FULL_HOUSE;
                }
                break;
            }
            case 4:
            {
                // AABCD
                if (labelJ != labelSize.end())
                    this->_type = HandType::THREE_OF_A_KIND;
                else
                    this->_type = HandType::ONE_PAIR;
                break;
            }
            case 5:
            {
                // ABCDE
                if (labelSize.find('J') != labelSize.end())
                    this->_type = HandType::ONE_PAIR;
                else
                    this->_type = HandType::HIGH_CARD;
                break;
            }
            default: break;
        }

        std::string digit {line.substr(6, line.size() - 6)};
        this->_bidAmount = std::stoi(digit);
    }

    bool operator < (const Hand& hand1, const Hand& hand2)
    {
        if (hand1.type() == hand2.type())
        {
            for (std::size_t i = 0; i < 5; ++i)
            {
                int hand1Strength {hand1.strengths()[i]};
                int hand2Strength {hand2.strengths()[i]};
                if (hand1Strength == hand2Strength)
                    continue;
                return hand1Strength > hand2Strength;
            }
            return false;
        }
        else
            return hand1.type() > hand2.type();
    }

    void solute()
    {
        std::ifstream file {"./src/day07/input.txt"};
        std::string line;
        std::vector<Hand> hands {};

        while (std::getline(file, line))
            hands.emplace_back(line);

        std::sort(hands.begin(), hands.end());

        // for (const Hand& hand : hands)
        //     std::cout << "type: " << static_cast<int>(hand.type()) 
        //               << " , bidAccount: " << hand.bidAmount() << std::endl;

        int result {0};
        for (int i = 0; i < hands.size(); ++i)
            result += ( (i + 1) * hands[i].bidAmount() );
        std::cout << result << std::endl;
    }

}